package com.study.web.action;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.study.common.security.BaseRes;
import com.study.common.security.EnumCode;
import com.study.common.security.KeyRepository;
import com.study.common.security.UserRSAkeyRes;
import com.study.common.utils.RequestUtil;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSON;
import com.study.common.exception.BizException;
import com.study.common.utils.ResponseUtil;
import com.study.common.utils.VerifyCodeUtils;
import com.study.web.context.SpringMVCActionSupport;
import com.study.web.entity.SysRole;
import com.study.web.entity.SysUser;
import com.study.web.requestVo.UserLoginRequestVo;
import com.study.web.service.sysrole.SysRoleService;
import com.study.web.service.sysuser.SysUserService;

/**
 * 
 * All rights Reserved, Designed By yaojinshan
 * @Title:  IndexAction.java   
 * @Package com.study.web.action   
 * @Description:  首页 控制器  
 * @author: yaojinshan     
 * @date:   2018年6月27日 下午3:35:27   
 * @version V1.0 
 * @Copyright: 2018 All users All rights reserved. 
 * 注意：注意：本内容仅限于学习springmvc + spring + maven + mybatis学习使用
 */
@RequestMapping("/")
@Controller
@SuppressWarnings("all")
public class IndexAction extends SpringMVCActionSupport{

	private static Logger logger = LoggerFactory.getLogger(IndexAction.class);
	@Resource
	private SysUserService sysUserService;
	@Resource
	private SysRoleService sysRoleService;
	@Resource
	private RedisTemplate redisTemplate;
	/**
	 * 跳转到登录页面
	 * @throws Exception 
	 * */
	@RequestMapping(value="/login")
	public String login(HttpServletRequest request,HttpServletResponse response) throws Exception{
		String parameter = request.getParameter("kickout");
		String flag = request.getParameter("flag");//flag 等于 1 是session丢失 然后重定向过来的请求 ， 等于 2 是访问项目根路径时的请求转发
		if(StringUtils.isNotBlank(parameter)){
			getHttpRequest().setAttribute("error", "您的账号已在别处登录，请确认是本人！");
		}else if("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))){
				Map<String,Object> resultMap = new HashMap<String,Object>();
				resultMap.put("result", ResponseUtil.RES_SESSION_LOST);
				resultMap.put("msg", "Login has expired or time out, please log on again!");
				response.getWriter().write(JSON.toJSONString(resultMap));
				return null;
		}
		return "login";
	}

	/**
	 * 执行登录
	 * */
	@RequestMapping(value="/exeLogin")
	@ResponseBody
	public String exeLogin(@Validated UserLoginRequestVo userLoginRequestVo){
		String result= ResponseUtil.RES_FAIL;
		Subject user = SecurityUtils.getSubject();
		UsernamePasswordToken token = new UsernamePasswordToken(userLoginRequestVo.getUserName(),/*password.toCharArray()*/ DigestUtils.md5Hex(userLoginRequestVo.getPassword()).toCharArray());
		Map<String, Object> map= new HashMap<String, Object>();
		Session session = SecurityUtils.getSubject().getSession();
		try {
			long end_VCodeTime = new Date().getTime();
			long start_VCodeTime = 0;
			start_VCodeTime = (Long) session.getAttribute(VerifyCodeUtils.V_CODE_STARTTIME);
			if(StringUtils.isBlank(userLoginRequestVo.getVerifyCode()) || (start_VCodeTime == 0l) || !VerifyCodeUtils.verifyCode(userLoginRequestVo.getVerifyCode()) || ((end_VCodeTime-start_VCodeTime)>60000) ){//一分钟有效期
				map.put("msg", "验证码错误或者已失效，请重新获取！");
			}else {
				if(userLoginRequestVo.getRememberMe()){//自动登录
					token.setRememberMe(true);
				}
				user.login(token);
				SysUser sysUser= (SysUser) SecurityUtils.getSubject().getPrincipal();
				sysUser.setLastLoginTime(sysUser.getLoginTime());
				sysUser.setLoginTime(new Date());
				sysUser.setLoginIp(getHttpRequest().getRemoteHost());
				sysUserService.update(sysUser);
				result=ResponseUtil.RES_SUCCESS;
				VerifyCodeUtils.clearVerifyCode();
			}
			
		} catch (UnknownAccountException e) {

			e.printStackTrace();
			map.put("msg", "账号不存在");
		} catch (LockedAccountException e) {
			e.printStackTrace();
			map.put("msg", e.getMessage());
		}catch (DisabledAccountException e) {
			e.printStackTrace();
			map.put("msg", "账号未启用");
		}  catch (IncorrectCredentialsException e) {
			e.printStackTrace();
			map.put("msg", "用户名密码错误");
		} catch (BizException e) {
			e.printStackTrace();
			result=e.getCode();
			map.put("msg", "系统异常,请联系管理员,错误代码："+e.getCode());
		}catch (RuntimeException e) {
			e.printStackTrace();
			map.put("msg", "未知错误,请联系管理员");
		}catch (Exception e) {
			e.printStackTrace();
			map.put("msg", "未知错误,请联系管理员");
		}finally {
	        session.setAttribute(VerifyCodeUtils.V_CODE, "");
	        session.setAttribute(VerifyCodeUtils.V_CODE_STARTTIME, "");
			map.put("result",result);
			return JSON.toJSONString(map);
		}
	}

	/**
	 * 主页
	 * */
	@RequestMapping(value="/welcome")
	public String welcome(){
		SysUser user= (SysUser) SecurityUtils.getSubject().getPrincipal();
		if(user == null){
			return "login";
		}
		SysRole systemRole = sysRoleService.getById(user.getRoleId());
		logger.info("-------------------> *欢迎访问maven web ssm 学习项目* <-------------------------------");
		logger.info("------------------->当前登录用户："+user.getLoginName()+"<-------------------------------");
		logger.info("------------------->当前登录用户所属角色："+systemRole.getZhName()+"<-------------------------------");
		getHttpRequest().getSession().setAttribute("user",user);
		getHttpRequest().getSession().setAttribute("systemRole",systemRole);
		return "index";
	}

	/**
	 * 退出
	 * */
	@RequestMapping(value="/logOut")
	public String logOut(){
		Subject subject = SecurityUtils.getSubject();
		subject.logout();
		return "login";
	}

	/**
	 * 没有相关权限
	 * */
	@RequestMapping(value="/unauth")
	public String unauth(){
		return "error/unauth";
	}

	/**
	 * 报错
	 * */
	@RequestMapping(value="/error")
	public String error(){
		return "error/error";
	}
	
	/**
	 * 获取验证码
	 * @param response
	 */
	@RequestMapping(value="getVCode",method=RequestMethod.GET)
	public void getVCode(HttpServletResponse response,HttpServletRequest request){
		try {
			response.setHeader("Pragma", "No-cache");  
	        response.setHeader("Cache-Control", "no-cache");  
	        response.setDateHeader("Expires", 0);  
	        response.setContentType("image/jpg");  	      
	        //生成随机字串  
	        String verifyCode = VerifyCodeUtils.generateVerifyCode(4);  
	        //存入Shiro会话session
	        Session session =SecurityUtils.getSubject().getSession();
	        session.setAttribute(VerifyCodeUtils.V_CODE, verifyCode.toLowerCase());
	        session.setAttribute(VerifyCodeUtils.V_CODE_STARTTIME, new Date().getTime());
	        //生成图片  
	        int w = 146, h = 33;  
	        VerifyCodeUtils.outputImage(w, h, response.getOutputStream(), verifyCode); 
		} catch (Exception e) {
			try {
				response.getWriter().write("获取验证码异常");
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}
	/**
	 * @Description 模拟获取当前session的验证码 <pre>
	 * @author 作者: yaojinshan <pre>
	 * @date 2018年6月27日 下午3:36:28 <pre> 
	 */
	@RequestMapping(value="getCodeValue")
	@ResponseBody
	public String getCodeValue(){
		//存入Shiro会话session
        Session session =SecurityUtils.getSubject().getSession();
		Object object = session.getAttribute(VerifyCodeUtils.V_CODE);
		return JSON.toJSONString(object);
	}

	/*
	　* @description: 测试返回空数据
	　* @author yaojinshan
	　* @date 2019/4/11 15:18
	　*/
    @RequestMapping(value="testReturnNull")
    @ResponseBody
    public void testReturnNull(){

    }

    /**
     * 获取用户的RSApublicKEY
     *
     * @return {"code":"success","ee":"10001","mm":"8c6e...35dc","tt":"1445930234006","kk":"1c6e...45dc"}
     * ee/mm加密必须
     * tt当前密钥生成时间戳,在调用接口时 将同步录入 tt=1445930234006 参数, 做校验. 预防单用户并发密钥更新,导致解码失败
     */
    @RequestMapping(value = "/getRsaKey")
    @ResponseBody
    public String getRsaKey(HttpServletRequest request) {
        UserRSAkeyRes res = new UserRSAkeyRes();
        try {
            KeyRepository key = initRSAMEbyUser(request);
            res.setEe(key.getExponent());
            res.setMm(key.getModulus());
            res.setTt(Long.toString(key.getGenerateDate().getTime()));
            key.setUk(UUID.randomUUID().toString());//做单次验证
            res.setKk(key.getUk());//
            res.setCode(EnumCode.success);
        } catch (Exception e) {
            e.printStackTrace();
            res.setCode(EnumCode.error);
        }
        return JSON.toJSONString(res);
    }

    /**
     * 异步登录
     *
     * @return
     * @throws Exception
     */
    @RequestMapping(value = "/ajaxLoginEXE")
    @ResponseBody
    public String ajaxLoginEXE(HttpServletRequest request) throws Exception {
        String returnUrl = "/welcome";
        BaseRes res = new BaseRes();
        String username = "";
        String password = "";
        String verifyCode = "";
        Boolean remember = false;
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("code", "error");
        try {
            Map<String, String[]> map = decryptRSAMEbyUserSafety(request);
            if (map.isEmpty()) {
                resultMap.put("msg", "参数为空，无法登陆！");
                return null;
            }
            username = RequestUtil.paraToFirst(map.get("username"));
            password = RequestUtil.paraToFirst(map.get("password"));
            verifyCode = RequestUtil.paraToFirst(map.get("verifyCode"));
            remember = Boolean.parseBoolean(RequestUtil.paraToFirst(map.get("remember")));
            if(StringUtils.isBlank(username)){
                resultMap.put("msg", "请输入登录名！");
                return null;
            }
            if(StringUtils.isBlank(password)){
                resultMap.put("msg", "请输入密码！");
                return null;
            }
            if(StringUtils.isBlank(verifyCode)){
                resultMap.put("msg", "请输入验证码！");
                return null;
            }
            Subject user = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5Hex(password).toCharArray());
            Long end_VCodeTime = new Date().getTime();
            Session session = SecurityUtils.getSubject().getSession();
            Long start_VCodeTime = (Long) session.getAttribute(VerifyCodeUtils.V_CODE_STARTTIME);
            if (StringUtils.isBlank(verifyCode) || !VerifyCodeUtils.verifyCode(verifyCode) || ((end_VCodeTime - start_VCodeTime) > 60000)) {//一分钟有效期
                resultMap.put("msg", "验证码错误或者已失效，请重新获取！");
            } else {
                if (remember) {//自动登录
                    token.setRememberMe(true);
                }
                user.login(token);
                SysUser sysUser = (SysUser) SecurityUtils.getSubject().getPrincipal();
                sysUser.setLastLoginTime(sysUser.getLoginTime());
                sysUser.setLoginTime(new Date());
                sysUser.setLoginIp(getHttpRequest().getRemoteHost());
                sysUserService.update(sysUser);
                res.setCode(EnumCode.success);
                VerifyCodeUtils.clearVerifyCode();
                resultMap.put("rurl", returnUrl);
                resultMap.put("code", "success");
            }
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            resultMap.put("msg", "账号不存在");
        } catch (LockedAccountException e) {
            e.printStackTrace();
            resultMap.put("msg", e.getMessage());
        } catch (DisabledAccountException e) {
            e.printStackTrace();
            resultMap.put("msg", "账号未启用");
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            resultMap.put("msg", "用户名或密码错误");
        } catch (BizException e) {
            e.printStackTrace();
            resultMap.put("msg", "系统异常,请联系管理员,错误代码：" + e.getCode());
        } catch (RuntimeException e) {
            e.printStackTrace();
            resultMap.put("msg", "未知错误,请联系管理员");
        } catch (Exception e) {
            e.printStackTrace();
            resultMap.put("msg", "未知错误,请联系管理员");
        } finally {
            return JSON.toJSONString(resultMap);
        }
    }
}
